package com.ku.consumerpay.service.impl;

import cn.hutool.core.util.IdUtil;
import cn.hutool.json.JSONUtil;
import com.ku.consumerpay.service.mq.CallBackService;
import com.ku.entity.CustomerAccount;
import com.ku.consumerpay.mapper.CustomerAccountMapper;
import com.ku.service.PayService;
import com.ku.consumerpay.service.mq.TransactionMQProvider;
import org.apache.dubbo.config.annotation.Service;
import org.apache.rocketmq.client.producer.LocalTransactionState;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.client.producer.TransactionSendResult;
import org.apache.rocketmq.common.message.Message;
import org.springframework.beans.factory.annotation.Autowired;

import java.math.BigDecimal;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;

/**
 * @program: rocketMQKill
 * @description:
 * @author: JokerKu
 * @create: 2020-05-06 21:04
 **/
@Service
public class PayServiceImpl implements PayService {

    @Autowired
    private CustomerAccountMapper customerAccountMapper;

    @Autowired
    private TransactionMQProvider mqProvider;

    @Autowired
    private CallBackService callBackService;

    public static final String TX_PAY_TOPIC = "tx_pay_topic";

    public static final String TX_PAY_TAGS = "pay";

    @Override
    public String payment(String userId, String orderId, String accountId, BigDecimal money) {

        //补充：在进入业务前，需要判断token，防止同一支付操作被发起多次
        //token可以在用户进入付款页面时生成，然后传入前端，同时保持到redis中，当第一次访问时，取出redis中的token进行比对，
        //随后删除

        String paymentResult = "";

        //极端情况，一个支付账户的支付操作可能被多个用户发起，所以可以加分布式锁，扣款时拿到锁才能操作
        //1.redis去重 分布式锁
        //2.数据库乐观锁去重

//        //每次扣减时查看订单的状态，如果未支付才走支付，否则直接返回
//       String orderStatus =  orderService.selectOrderStatus(orderId);
//       if (!"1".equals(orderStatus)){
//           return "支付失败，请重试!";
//       }

        try {
            //--------加锁-------没有获取到锁的线程就自旋等待

            //查询账号余额
            CustomerAccount customerAccount = customerAccountMapper.selectByPrimaryKey(accountId);
            BigDecimal currentBalance = customerAccount.getCurrentBalance();
            Integer version = customerAccount.getVersion();

            //判断扣减后余额是否大于0
            BigDecimal newBalance = currentBalance.subtract(money);
            if (newBalance.doubleValue() > 0) {
                //如果扣减后余额是够的：
                //1.组装消息发送到MQ（发送到MQ增加平台账户余额），同时执行本地事务

                //1.1生成消息的key
                final String KEYS = IdUtil.randomUUID();
                //1.2封装消息
                Map<String, Object> params = new HashMap<>();
                params.put("userId", userId);
                params.put("orderId", orderId);
                params.put("accountId", accountId);
                params.put("money", money);
                //封装
                Message message = new Message(TX_PAY_TOPIC, TX_PAY_TAGS, KEYS, JSONUtil.toJsonStr(params).getBytes(Charset.defaultCharset()));

                //本地事务可能需要使用到的属性
                params.put("newBalance", newBalance);
                params.put("currentVersion", version);
                //调用MQ发送消息
                TransactionSendResult sendResult = mqProvider.sendMessage(message, params);

                //查询消息投递结果，如果投递到Consumer成功，则修改订单状态
                if (sendResult.getSendStatus() == SendStatus.SEND_OK
                        && sendResult.getLocalTransactionState() ==
                        LocalTransactionState.COMMIT_MESSAGE) {
                    //----修改订单状态---使用MQ将消息投递-
                    callBackService.sendOKMessage(orderId,userId);

                    paymentResult = "支付成功";
                }else {
                    paymentResult = "支付失败";
                }
            }else {
                paymentResult = "余额不足";
            }

            //--------释放锁-----------

            return paymentResult;

        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
